/**
 * 
 */
package com.game.jumpdino.logic.visual;

import java.util.ArrayList;
import java.util.List;

import com.game.jumpdino.JumpingView;
import com.game.jumpdino.logic.Playground;
import com.game.jumpdino.logic.Velocity;
import com.game.jumpdino.logic.VisualConfig;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.AnimationDrawable;
import android.graphics.drawable.Drawable;
import android.media.MediaPlayer;

/**
 * @author guhu
 *
 */
public class VisualBase {
	public static final float defaultJumpSpeed = -23;
	private static float minSpeedUp = defaultJumpSpeed;
	private List<VisualBase> follows = new ArrayList<VisualBase>();
	private VisualBase attachTo;
	private Playground playground;
	protected Context mContext;
	private int effectTimes = 0;
	protected float speedUpFactor;
	protected Drawable image;
	protected int maxVx;
	protected int maxVy;
	private Velocity v;
	protected Rect position;
	private VisualConfig mProperties;
	private float lastMoveY;
	protected boolean bonusOnEdge = true;
	protected boolean isVisible = true;
	protected boolean isValid;
	private float g;
	protected float gx;
	private int maxTimes;
	
	public VisualBase(){
		position = new Rect();
		v = new Velocity(0, 0);
		setG(0.0f);
		isValid = true;
		maxVy = 100;
		maxVx = 10;
	}
	
	/**
	 * @return the minSpeedUp
	 */
	public static float getMinSpeedUp() {
		return minSpeedUp;
	}
	
	public boolean isVisible() {
		return isVisible;
	}
	
	public void setVisible(boolean isVisible) {
		this.isVisible = isVisible;
	}

	/**
	 * @param minSpeedUp the minSpeedUp to set
	 */
	public static void setMinSpeedUp(float minSpeedUp) {
		VisualBase.minSpeedUp = minSpeedUp;
	}
	
	public void follow(VisualBase follow){
		this.setAttachTo(follow);
		follow.getFollows().add(this);
	}
	
	public void unFollow(VisualBase follow){
		if (getAttachTo() != null){
			follow.getFollows().remove(this);
		}
		
		setAttachTo(null);
	}

	public Rect getAbsoluteHitArea(){
		if (mProperties.hitTestArea.isEmpty()){
			return position;
		} else {
			return new Rect(position.left + mProperties.hitTestArea.left,
					position.top + mProperties.hitTestArea.top,
					position.left + mProperties.hitTestArea.right,
					position.top + mProperties.hitTestArea.bottom);			
		}
	}
	
	public void setMaxTimes(int times){
		maxTimes = times;
	}
	
	public int getMaxTimes(){
		return maxTimes;
	}
	
	public Velocity getVelocity(){
		return v;
	}
	
	public boolean isValid(){
		return isValid;
	}
	
	public VisualConfig getProperties(){
		return mProperties;
	}
	
	public void setProperties(VisualConfig properties){
		if (mProperties == null){
			mProperties = properties;
		} else {
			throw new RuntimeException("Cannot change Coniguration instance.");
		}		
	}
	
	public Velocity updateVelocity(float Vx, float Vy){
		// Do nothing and wait for the attach element to update this position.
		if (getAttachTo() != null){
			return v;
		}
		
		v.x += Vx;
		v.y += Vy;
		
		if (v.x > maxVx){
			v.x = maxVx;
		}
		if (v.y > maxVy){
			v.y = maxVy;
		}
		
		return v;
	}
	
	public void setVy(float y){
		// Do nothing and wait for the attach element to update this position.
		if (getAttachTo() != null){
			return;
		}
		
		if (y > maxVy){
			v.y = maxVy;
		} else {
			v.y = y;
		}
	}
	
	public void setVx(float x){
		// Do nothing and wait for the attach element to update this position.
		if (getAttachTo() != null){
			return;
		}
		
		v.x = x;
	}
	
	public Drawable getImage(){
		return image;
	}
	
	public Drawable setImage(Drawable newImage){
		Drawable previousImage = this.image;
		if (newImage != null){			
			this.image = newImage;
			//if (mContext.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT){
				position.right = position.left + image.getIntrinsicWidth();
				position.bottom = position.top + image.getIntrinsicHeight();
			//} else {
			//	position.right = position.left + image.getIntrinsicHeight();
			//	position.bottom = position.top + image.getIntrinsicWidth();				
			//}
		}
		
		return previousImage;
	}
	
	public void setRenderSize(int width, int height){
		if (width != 0 && height != 0){
			position.right = position.left + width;
			position.bottom = position.top + height;
		} else {
			// Do nothing. use the image intrinsic size.
		}
	}
	
	public Rect getPosition(){
		return position;
	}
	
	public void doMove(){
		// Do nothing and wait for the attach element to update this position.
		if (getAttachTo() != null){
			return;
		}
		
		if (v.x > 0){
			v.x -= gx;
		} else if (v.x < 0){
			v.x += gx;
		}
		v.y += getG();
		move(v.x, v.y);
	}
	
	public void move(float x, float y){
		// Do nothing and wait for the attach element to update this position.
		if (getAttachTo() != null){
			return;
		}
		
		// Make sure the move within the screen left and right boundary.
		float offsetX = x;
		if (position.right + offsetX > Playground.getWidth()){
			if (bonusOnEdge){
				this.setVx(-v.x);
				onTurnAround();
			} else {
				this.setVx(0.0f);
			}
			offsetX = Playground.getWidth() - position.right; 
		}
		if (position.left + offsetX < 0){
			if (bonusOnEdge){
				this.setVx(-v.x);
				onTurnAround();
			} else {
				this.setVx(0.0f);
			}
			offsetX = -position.left;
		}
		lastMoveY = y;
		
		for (VisualBase follow : getFollows()){
			follow.position.offset(Math.round(offsetX), Math.round(lastMoveY));
			follow.lastMoveY = y;
		}
		onMove(offsetX, lastMoveY);
		position.offset(Math.round(offsetX), Math.round(lastMoveY));
	}
	
	public void setSpeedUp(float value){
		speedUpFactor = value;
	}
	
	public void onTurnAround(){
		
	}
	
	public void onMove(float offsetX, float offsetY){
		if (mProperties.mVisualEventListener != null){
			mProperties.mVisualEventListener.onMove(this, offsetX, offsetY);
		}
	}
	
	public void drawOn(Canvas c){
		if (isVisible && image != null){
			image.setBounds(position);
			if (image instanceof AnimationDrawable){
				image.setCallback(JumpingView.getCurrent());
				image.draw(c);
				((AnimationDrawable) image).start();
			} else {
				image.draw(c);
			}
		}
	}
	
	public boolean hit(VisualBase visual){
		return Rect.intersects(getAbsoluteHitArea(), visual.getAbsoluteHitArea());
	}
	
	public boolean hitTop(VisualBase visual){
		Rect visualHitArea = visual.getAbsoluteHitArea();
		Rect thisHitArea = getAbsoluteHitArea();
		if (visualHitArea.top >= thisHitArea.top &&
			visualHitArea.top <= thisHitArea.bottom){
			return Rect.intersects(visualHitArea, thisHitArea);
		}
		
		return false;
	}
	
	public boolean hitBottom(VisualBase visual){
		Rect visualHitArea = visual.getAbsoluteHitArea();
		Rect thisHitArea = getAbsoluteHitArea();
		if (visualHitArea.bottom >= thisHitArea.top &&
			visualHitArea.bottom - visual.lastMoveY <= thisHitArea.bottom){
			return Rect.intersects(visualHitArea, thisHitArea);
		}
		
		return false;
	}
	
	public void onHit(VisualBase visual, Playground ground){
		playSound(ground.getContext());
		
		if (mProperties.mVisualEventListener != null){
			mProperties.mVisualEventListener.onHit(this, visual, ground);
		}
	}
	
	public void onInitialized(Context context){
		this.mContext = context;
	}
	
	public void doJump(VisualBase jumper){
		float speed = jumper.getVelocity().y;
		if (speed >= 0)
		{
			if ((speed > -minSpeedUp * speedUpFactor) && (speedUpFactor > 0)){
				jumper.setVy(-speed);
			} else {
				jumper.setVy(minSpeedUp * speedUpFactor);
			}
			++effectTimes;		
			if (getMaxTimes() > 0 && effectTimes >= getMaxTimes()){
				isValid = false;
			}
		}
	}
	
	public void putOnTopOf(VisualBase holder){
		// 17 is the margin of the panda and the bamboo in picture.
		float offsetY = holder.getPosition().top - getPosition().bottom + 5;
		if (offsetY < 0){
			float offsetX = 0;// this.getVelocity().x * offsetY / this.getVelocity().y;
			this.move(offsetX, offsetY);
		} else {
			// Do nothing.
		}
	}
	
	public void playSound(Context context){
		/* tony comment out and try out 
		if (mProperties.sndID != 0){
			MediaPlayer player = MediaPlayer.create(context, mProperties.sndID);
			player.start();
	
		}	*/
	}

	public void setAttachTo(VisualBase attachTo) {
		this.attachTo = attachTo;
	}

	public VisualBase getAttachTo() {
		return attachTo;
	}

	public void setG(float g) {
		this.g = g;
	}

	public float getG() {
		return g;
	}

	public void setFollows(List<VisualBase> follows) {
		this.follows = follows;
	}

	public List<VisualBase> getFollows() {
		return follows;
	}

	public void setPlayground(Playground playground) {
		this.playground = playground;
	}

	public Playground getPlayground() {
		return playground;
	}
}